﻿/*
* @Author: z.p
* @Project: Zdotp
* @Date: 2024年12月31日
* @Description: 通过system进行actor发布订阅的通信, 该system进程唯一
*/

using System.Reflection;
using Google.Protobuf;
using Zdotp.Actor.Components;
using Zdotp.Actor.Controller;
using Zdotp.Actor.Group;
using Zdotp.Actor.Kcp;
using Zdotp.Actor.Packet;
using Zdotp.Logs;
using Zdotp.Net.NetMessageHandler;
using Zdotp.ObjectPool;
using Zdotp.Pb;
using Zdotp.Timer;

namespace Zdotp.Actor;

public class ActorSystem
{
    private readonly ZLogger _logger;
    public readonly string SystemName;

    public ActorSystem(string systemName)
    {
        SystemName = systemName;
        _logger = LogHelper.CreateLogger(SystemName);
        LogHelper.PrintBigMessage("Zdotp");
    }
    private readonly Dictionary<Type, List<int>> _observers = new();
    private readonly List<ActorBase> _actors = [];
    private readonly List<ZGroup> _groups = [];

    private int _maxActorId;
    
    public void AddActor(Func<ActorBase> creator, int groupId, float deltaTime = 0.1f)
    {
        var actor = creator?.Invoke();
        if (actor == null)
        {
            _logger.Error($"{SystemName} cannot create actor");
            return;
        }

        var group = _groups.FirstOrDefault(myGroup => myGroup.GroupId == groupId);
        if (group == null)
        {
            group = new ZGroup(groupId, deltaTime);
            _groups.Add(group);
        }
        group.AddActor(actor);
        
        actor.Awake(++ _maxActorId, this, group);
        _actors.Add(actor);
        
        var actorType = actor.GetType();
        var componentAttributes = actorType.GetCustomAttributes<ComponentAttribute>();
        foreach (var componentAttribute in componentAttributes)
        {
            var componentType = componentAttribute.ComponentType;
            actor.AddComponent(componentType);
        }
    }
    
    public void ReceiveMessage<T>(T data, int senderActorId = -1, int receiverActorId = -1, uint sessionId = 0) where T : MailObject
    {
        var packetType = typeof(T).Name;
        if (!_observers.TryGetValue(typeof(T), out var observer))
        {
            _logger.Warn($"{SystemName} cannot find observer for packet type: {packetType}");
            return;
        }

        switch (receiverActorId)
        {
            // 转发给所有actor
            case -1:
            {
                foreach (var actId in observer)
                {
                    ActorBase? actor = null;
                    foreach (var actorBase in _actors)
                    {
                        if (actorBase.ActorId != actId) continue;
                        actor = actorBase;
                        break;
                    }

                    if (actor == null)
                    {
                        _logger.Error($"cannot find actor for packet type: {packetType}");
                        break;
                    }
                    actor.ReceiveMessage(data, senderActorId, sessionId);
                }
                break;
            }
            // 转发给指定actor
            case > -1:
            {
                ActorBase? actor = null;
                foreach (var actId in observer)
                {
                    if (actId == receiverActorId)
                    {
                        foreach (var actorBase in _actors)
                        {
                            if (actorBase.ActorId != actId) continue;
                            actor = actorBase;
                            break;
                        }
                        break;
                    }
                }

                if (actor == null)
                {
                    var index = receiverActorId % observer.Count;
                    var actorId = observer[index];
                    foreach (var actorBase in _actors)
                    {
                        if (actorBase.ActorId != actorId) continue;
                        actor = actorBase;
                        break;
                    }
                }

                if (actor == null)
                {
                    _logger.Error($"cannot find actor for packet type: {packetType}");
                    break;
                }
                actor.ReceiveMessage(data, senderActorId, sessionId);
                break;
            }
            default:
                _logger.Error($"actorId is illegal! : {receiverActorId} ");
                break;
        }
    }
    
    public void RegisterMessage(Type packetType, int actorId)
    {
        if (!_observers.ContainsKey(packetType)) _observers.Add(packetType, []);
        _observers[packetType].Add(actorId);
    }
    
    public void Start(List<Assembly> assemblies)
    {
        assemblies.Add(GetType().Assembly);
        
        ActorControllerManager.LoadAssemblies(assemblies);
        KcpNetHandlerManager.LoadAssemblies(assemblies);
        ObjectPoolManager.LoadAssemblies(assemblies);
        
        foreach (var group in _groups) group.Start();
        while (true)
        {
            var readLine = Console.ReadLine();
            if (readLine == "exit") break;
        }
        
        foreach (var group in _groups) group.Destroy();
    }

    public static void ActorControl(Type messageType, ActorBase actor, MessageInfo info, MailObject message)
    {
        ActorControllerManager.Control(messageType, actor, info, message);
    }

    public static void NetHandle(uint protocolId, ActorKcp actor, uint sessionId, byte[] message)
    {
        KcpNetHandlerManager.Handle(protocolId, actor, sessionId, message);
    }

    public static uint NetGetProtocolId(Type type) => KcpNetHandlerManager.GetProtocolId(type);
    public static T? GetObj<T>(Action<T>? init = null, Action<T>? callback = null) where T : ObjectBase, new() => ObjectPoolManager.GetObj(init, callback);
    public static void ReleaseObj<T>(T obj) where T : ObjectBase, new() => ObjectPoolManager.ReleaseObj(obj);
    public static void ReleaseObj(Type objectType, ObjectBase obj) => ObjectPoolManager.ReleaseObj(objectType, obj);

    public ZTimer CreateTimer(Action<ZTimer>? init = null, Action<ZTimer>? overCallback = null)
    {
        var timer = GetObj(init, overCallback);
        if (timer == null)
        {
            timer = new ZTimer();
            // 如果是new出来的 就不需要回收!
            _logger.Error($"CreateTimer fail, but new");
            init?.Invoke(timer);
        }
        return timer;
    }

    public T CreateMail<T>(Action<T>? init = null) where T : MailObject, new()
    {
        var mail = GetObj(init);
        if (mail != null) return mail;
        
        _logger.Error($"Mail init fail");
        return new T();
    }

    public static byte[] GetNetPacket<T>(T data) where T : IMessage<T>
    {
        var protocolId = NetGetProtocolId(typeof(T));
        var dataPacket = data.Serialize();
        var bytes = new byte[4 + dataPacket.Length];
        Array.Copy(BitConverter.GetBytes(protocolId), 0, bytes, 0, 4);
        Array.Copy(dataPacket, 0, bytes, 4, dataPacket.Length);
        return bytes;
    }
}